package com.tools.web.interceptor.quota;

import com.tools.common.object.Force;
import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import com.tools.db.cache.local.LocalCache;

/**
 * 使用本地缓存实例 LocalCache 实现配额限制拦截器
 * 【注意】：业务成功后必须通过该拦截器实例的 incrSuccessfulCount 方法进行次数自增。
 * */
@Note("使用本地缓存实例 LocalCache 实现配额限制拦截器." +
        "【注意】：业务成功后必须通过该拦截器实例的 incrSuccessfulCount 方法进行次数自增。")
public final class LocalCacheQuotaInterceptor extends QuotaInterceptor {

    @Note("本地缓存实例")
    private final LocalCache cache;

    @Note("当本地缓存 LocalCache 将源值传入时，将源值 + 1 返回给缓存进行更新。" +
            "整个过程在 LocalCache 实例的内部完成，属于原子性操作。" +
            "【注意】：源值和新值一样的话由于 LocalCache 更新方法的幂等校验会不予更新。")
    private static final Force<Integer> CALC_MODIFY_VALUE;

    public LocalCacheQuotaInterceptor(LocalCache cache, QuotaSource source) {
        super(source, cache != null, "本地缓存实例不能为 null");
        this.cache = cache;
    }

    static {
        CALC_MODIFY_VALUE = new Force<Integer>() {
            @Override
            public Integer force(Integer source) {
                if(source == null || source <= 0) return 1;
                return source + 1;
            }
        };
    }

    @Override
    public void incrSuccessfulCount(String uniqueID, String action, long createExpiration, Timeunit createTimeunit) {
        if(createExpiration < 1) return;
        if(createTimeunit == null) throw new NullPointerException("过期时间单位为 null");
        String key = super.protected_getCachePrefix() + action + ':' + uniqueID;
        this.cache.set(key, CALC_MODIFY_VALUE, 1, Timeunit.toMill(createExpiration, createTimeunit));
    }

    @Override
    protected Integer protected_getSuccessfulCount(String key) {
        return this.cache.get(key, Integer.class);
    }

    @Override
    protected long protected_getRemainder(String key) {
        return this.cache.ttl(key);
    }
}
